package nachos.threads;

import nachos.machine.*;

/**
 * A multi-threaded OS kernel.
 */
public class ThreadedKernel extends Kernel
{
    /**
     * Allocate a new multi-threaded kernel.
     */
    public ThreadedKernel()
    {
        super();
    }

    /**
     * Initialize this kernel. Creates a scheduler, the first thread, and an
     * alarm, and enables interrupts. Creates a file system if necessary.
     */
    public void initialize(String[] args)
    {
        // set scheduler
        String schedulerName = Config.getString("ThreadedKernel.scheduler");
        scheduler = (Scheduler) Lib.constructObject(schedulerName);

        // set fileSystem
        String fileSystemName = Config.getString("ThreadedKernel.fileSystem");
        if (fileSystemName != null)
            fileSystem = (FileSystem) Lib.constructObject(fileSystemName);
        else if (Machine.stubFileSystem() != null)
            fileSystem = Machine.stubFileSystem();
        else
            fileSystem = null;

        // start threading
        new KThread(null);

        alarm = new Alarm();

        Machine.interrupt().enable();
    }

    /**
     * Test this kernel. Test the <tt>KThread</tt>, <tt>Semaphore</tt>,
     * <tt>SynchList</tt>, and <tt>ElevatorBank</tt> classes. Note that the
     * autograder never calls this method, so it is safe to put additional
     * tests here.
     */
    public void selfTest()
    {
//        KThread.joinTest();
//        KThread.PriorityTest();
//        KThread.selfTest();
//        AlarmTest();
//        SpeakTest();
//        PriorityScheduler.selfTest();
//        Boat.selfTest();
//        Semaphore.selfTest();
//        SynchList.selfTest();
//        if (Machine.bank() != null)
//        {
//            ElevatorBank.selfTest();
//        }
    }


    /**
     * A threaded kernel does not run user programs, so this method does
     * nothing.
     */
    public void run()
    {
    }

    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate()
    {
        Machine.halt();
    }

    /**
     * Globally accessible reference to the scheduler.
     */
    public static Scheduler scheduler = null;
    /**
     * Globally accessible reference to the alarm.
     */
    public static Alarm alarm = null;
    /**
     * Globally accessible reference to the file system.
     */
    public static FileSystem fileSystem = null;

    // dummy variables to make javac smarter
    private static RoundRobinScheduler dummy1 = null;
    private static PriorityScheduler dummy2 = null;
    private static LotteryScheduler dummy3 = null;
    private static Condition2 dummy4 = null;
    private static Communicator dummy5 = null;
    private static Rider dummy6 = null;
    private static ElevatorController dummy7 = null;


    public static void AlarmTest(){
        KThread a = new KThread(new Runnable() {
            public void run() {
                System.out.println("线程a启动");
                for(int i = 0;i<5;i++){
                    if(i == 2){
                        System.out.println("线程a sleep，now："+Machine.timer().getTime()+", expect: after 1000 clicks");
                        Alarm.alarm().waitUntil(1000);
                        System.out.println("线程a wake，now："+Machine.timer().getTime());
                    }
                    System.out.println("*** thread 1 looped "
                            + i + " times");
//                    KThread.currentThread().yield();
                }
            }
        });
        a.fork();
        System.out.println("\n测试Alarm：");
        for(int i = 0;i<5;i++){
            if(i == 2){
                System.out.println("thread main sleep，now："+Machine.timer().getTime()+", expect: after 8000 clicks");
                Alarm.alarm().waitUntil(8000);
                System.out.println("thread wake, now："+Machine.timer().getTime());
            }
            System.out.println("*** thread 0 looped "
                    + i + " times");
            KThread.currentThread().yield();
        }
    }

    private static class Speaker implements Runnable {
        private Communicator c;
        Speaker(Communicator c) {
            this.c = c;
        }
        public void run() {
            for (int i = 0; i < 5; ++i) {
                System.out.println("speaker speaking word:" + i);
                Alarm.alarm().waitUntil(100000);
                c.speak(i);
                //System.out.println("speaker spoken");
                KThread.yield();
            }
        }
    }
    public static void SpeakTest() {
        System.out.println("\n测试Communicator类：");
        Communicator c = new Communicator();
        new KThread(new Speaker(c)).setName("Speaker").fork();
        for (int i = 0; i < 5; ++i) {
            System.out.println("listener listening " + i);
            int x = c.listen();
            System.out.println("listener listened, word = " + x);
            KThread.yield();
        }
    }


}
